#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <unordered_map>

static const std::string LineSEP = "\r\n";
static const std::string InLineSEP = " ";
static const std::string HeaderSEP = ": ";
static const std::string wwwRoot = "./wwwroot";
static const std::string defaultHtml = "index.html";

class DeCode
{
public:
    DeCode()
    {}
    bool CheckRequestLine()
    {
        std::stringstream ss(_requestLine);
        ss >> _method >> _url >> _version;
        if(_method.empty() || _url.empty() || _version.empty())
            return false;
        //... do others
        return true;
    }

    std::string operator()(std::string& inbuffer)
    {
        
        int pos = 0, oldpos = 0, totalLength = 0;
        pos = inbuffer.find(LineSEP);
        if(pos == std::string::npos) return "";
        _requestLine = inbuffer.substr(oldpos, pos);
        if(_requestLine.empty()) return "";
        
        if(!CheckRequestLine()) return "";
        totalLength = pos + LineSEP.size();
        oldpos = totalLength;

        while(true)
        {
            pos = inbuffer. find(LineSEP, oldpos);
            if(pos == std::string::npos) return "";

            //获取单行
            std::string message = inbuffer.substr(oldpos, pos - oldpos);
            //处理单行

            if(message == "")
            {
                totalLength += LineSEP.size();
                oldpos = totalLength;
                break;
            }

            auto headerpos = message.find(HeaderSEP);
            if(headerpos == std::string::npos) return "";
            std::string k = message.substr(0, headerpos);
            std::string v = message.substr(headerpos + HeaderSEP.size());
            if(k.empty() || v.empty()) return "";

            if(k == "Content-Length")
                _contentLength = std::stoi(v);

            totalLength += message.size() + LineSEP.size();
            oldpos = totalLength;
        }

        totalLength += _contentLength;
        if(totalLength > inbuffer.size())
            return "";
        std::string ret = inbuffer.substr(0, totalLength);
        inbuffer.erase(0, totalLength);
        // std::cout << ret;
        return ret;
    }
    ~DeCode()
    {}
private:
    std::string _requestLine;
    std::string _reqMessage;

    int _contentLength = 0;
    std::string _method;
    std::string _url;
    std::string _version;
};

class Request
{
    std::string ParseLine(std::string &s)
    {
        auto pos = s.find(LineSEP);
        if (pos == std::string::npos)
            return s;
        std::string ret = s.substr(0, pos);
        s.erase(0, pos + LineSEP.size());
        return ret;
    }
    void ParseReqLine()
    {
        std::stringstream ss(_requestLine);
        ss >> _method >> _url >> _version;
        
        _path = wwwRoot + _url;
        if(_path.back() == '/')
            _path += defaultHtml;
        
        auto pos = _path.rfind('.');
        if(pos == std::string::npos)
            _suffix = ".default";
        else
            _suffix = _path.substr(pos);

    }
    void ParseHeader()
    {
        for (const auto &str : _requestHeader)
        {
            auto pos = str.find(HeaderSEP);
            std::string k = str.substr(0, pos);
            std::string v = str.substr(pos + HeaderSEP.size());

            _kv[k] = v;
        }
    }

public:
    Request()
        : _blankLine(LineSEP)
    {
    }
    void DeSerialize(std::string in)
    {
        _requestLine = ParseLine(in);
        std::string message;
        while ((message = ParseLine(in)) != "")
        {
            _requestHeader.emplace_back(message);
        }
        _reqMessage = in;

        ParseReqLine();
        ParseHeader();
    }
    std::string suffix()
    {
        return _suffix;
    }
    std::string path()
    {
        return _path;
    }
    ~Request()
    {
    }

private:
    std::string _requestLine;
    std::vector<std::string> _requestHeader;
    std::string _blankLine;
    std::string _reqMessage;

    std::string _method;
    std::string _url;
    std::string _path; //文件路径
    std::string _suffix; //文件后缀
    std::string _version;
    std::unordered_map<std::string, std::string> _kv;
};

class Response
{
public:
    Response()
        :_blankLine(LineSEP)
    {
        _code_descript[200] = "OK";
        _code_descript[301] = "Moved Permanently";
        _code_descript[302] = "Found";
        _code_descript[404] = "Not Found";

        _content_type[".default"] = "text/html";
        _content_type[".html"] = "text/html";
        _content_type[".png"] = "image/png";
        _content_type[".jpg"] = "image/jpeg";
        _content_type[".ico"] = "application/x-ico";
    }
    void Serialize(std::string &out)
    {
        _statusLine = _version + InLineSEP + std::to_string(_statusCode) + InLineSEP +_statusDescript;
        for (auto& e : _kv)
            _responseHeader.emplace_back(e.first + HeaderSEP + e.second);
        
        out += _statusLine + LineSEP;
        for (auto& str : _responseHeader)
            out += str + LineSEP;
        out += _blankLine;
        out += _resMessage;
    }
    void AddStatus(int code)
    {
        _statusCode = code;
        _statusDescript = _code_descript[_statusCode];
    }
    void AddContentType(const std::string suffix)
    {
        Addkv("Content-Type", _content_type[suffix]);
    }
    void Addkv(const std::string& k, const std::string& v)
    {
        _kv[k] = v;
    }
    void AddContent(const std::string& message)
    {
        _resMessage = message;
        Addkv("Content-Length", std::to_string(message.size()));
    }
    ~Response()
    {
    }

private:
    std::string _statusLine;
    std::vector<std::string> _responseHeader;
    std::string _blankLine;
    std::string _resMessage;

    std::string _version = "HTTP/1.0";
    int _statusCode = 200;
    std::string _statusDescript = "OK";
    std::unordered_map<std::string, std::string> _kv;
    std::unordered_map<int, std::string> _code_descript;
    std::unordered_map<std::string, std::string> _content_type;
};